/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef COMPILER_OPTIMIZER_TEMPLATES_IR_DYN_BASE_TYPES_H
#define COMPILER_OPTIMIZER_TEMPLATES_IR_DYN_BASE_TYPES_H

#include "compiler/optimizer/ir/datatype.h"
#include "compiler/optimizer/code_generator/encode.h"
#include "profiling/profiling.h"
#include "source_languages.h"

% Common::plugins.each_value do |plugin_opts|
% next unless plugin_opts["compiler_base_types"]
% next unless plugin_opts["compiler_base_types"]["header_path_implementation_codegen"]
#include "<%= plugin_opts["compiler_base_types"]["header_path_implementation_codegen"] %>"
% end

% Common::plugins.each_value do |plugin_opts|
% next unless plugin_opts["compiler_extensions"]
% next unless plugin_opts["compiler_extensions"]["main_header_path"]
#include "<%= plugin_opts["compiler_extensions"]["main_header_path"] %>"
% end

namespace panda::compiler {

class CompareAnyTypeInst;
class CastAnyTypeValueInst;
class CastValueToAnyTypeInst;
class GetAnyTypeNameInst;
class AnyTypeCheckInst;
class EncodeVisitor;
class Inst;
class FixedInputsInst2;
class HclassCheckInst;

inline AnyBaseType NumericDataTypeToAnyType([[maybe_unused]] panda::compiler::DataType::Type type,
                                            panda::SourceLanguage language) {
    ASSERT(type == panda::compiler::DataType::Type::UINT8 || type == panda::compiler::DataType::Type::INT8 ||
           type == panda::compiler::DataType::Type::UINT16 || type == panda::compiler::DataType::Type::INT16 ||
           type == panda::compiler::DataType::Type::UINT32 || type == panda::compiler::DataType::Type::INT32 ||
           type == panda::compiler::DataType::Type::UINT64 || type == panda::compiler::DataType::Type::INT64 ||
           type == panda::compiler::DataType::Type::FLOAT32 || type == panda::compiler::DataType::Type::FLOAT64);

    switch(language) {  // NOLINT(hicpp-multiway-paths-covered)
% Common::plugins.each do |plugin_lang, plugin_opts|
% next unless plugin_opts["language_config"]["lang_type"] == "dynamic"
% next unless plugin_opts["compiler_base_types"]
% next unless plugin_opts["compiler_base_types"]["func_resolve_numeric_type"]
    case panda::SourceLanguage::<%= plugin_lang %>:
        return <%= plugin_opts["compiler_base_types"]["func_resolve_numeric_type"] %>(type);
% end
    default:
        ASSERT(false && "This feature is not supported for this language");
        return AnyBaseType::UNDEFINED_TYPE;
    }
}

inline AnyBaseType GetAnyStringType(panda::SourceLanguage language) {
    switch(language) {  // NOLINT(hicpp-multiway-paths-covered)
% Common::plugins.each do |plugin_lang, plugin_opts|
% next unless plugin_opts["language_config"]["lang_type"] == "dynamic"
% next unless plugin_opts["compiler_base_types"]
% next unless plugin_opts["compiler_base_types"]["func_resolve_string_type"]
    case panda::SourceLanguage::<%= plugin_lang %>:
        return <%= plugin_opts["compiler_base_types"]["func_resolve_string_type"] %>();
% end
    default:
        ASSERT(false && "This feature is not supported for this language");
        return AnyBaseType::UNDEFINED_TYPE;
    }
}

/*
 * Checks that the exact type of `any` value that is subtype of `type` also can be subtype of `super_type`.
 * Returns `true` if the `type` is equal to or subtype of `super_type` (i.e `type` is `STRING_TYPE` and
 * `super_type` is `OBJECT_TYPE`).
 * Returns `false` if there is no subtype relationship beetween `type` an `super_type` (i.e `type` is `INT_TYPE`
 * and `super_type` is `OBJECT_TYPE`)
 * Return `nullopt` if the `super_type` is subtype of `type` (i.e `type` is `OBJECT` and `super_type` is `STRING`).
 * In this case we need to check exact type at the runtime.
 */
inline std::optional<bool> IsAnyTypeCanBeSubtypeOf([[maybe_unused]] panda::SourceLanguage language,
                                                   [[maybe_unused]] AnyBaseType super_type,
                                                   [[maybe_unused]] AnyBaseType type,
                                                   [[maybe_unused]] profiling::AnyInputType super_allowed_types = profiling::AnyInputType::DEFAULT,
                                                   [[maybe_unused]] profiling::AnyInputType allowed_types = profiling::AnyInputType::DEFAULT) {
    switch(language) {  // NOLINT(hicpp-multiway-paths-covered)
% Common::plugins.each do |plugin_lang, plugin_opts|
% next unless plugin_opts["language_config"]["lang_type"] == "dynamic"
% next unless plugin_opts["compiler_base_types"] != nil
% next unless plugin_opts["compiler_base_types"]["func_is_any_type_can_be_subtype_of"] != nil
    case panda::SourceLanguage::<%= plugin_lang %>:
        return <%= plugin_opts["compiler_base_types"]["func_is_any_type_can_be_subtype_of"] %>(super_type, type, super_allowed_types, allowed_types);
% end
    default:
        ASSERT(false && "This feature is not supported for this language");
        return false;
    }
}

inline panda::compiler::DataType::Type AnyBaseTypeToDataType([[maybe_unused]] AnyBaseType any_type) {
    switch(any_type) {
        case AnyBaseType::UNDEFINED_TYPE:
            return panda::compiler::DataType::Type::ANY;
% Common::plugins.each do |plugin_lang, plugin_opts|
% next unless plugin_opts["compiler_base_types"]
% next unless plugin_opts["compiler_base_types"]["list_types"]
%   has_bool_type = false
%   plugin_opts["compiler_base_types"]["list_types"].each do |type_name, type_pair|
        case AnyBaseType::<%= plugin_lang %>_<%= type_name %>:
            return <%= type_pair %>;
%     if (type_pair.include?("BOOL"))
%       if (has_bool_type)
%         abort 'Compiler does not support two dynamic boolean types(See example in Peephole::VisitIfImm)'
%       end
%       has_bool_type = true
%     end
%   end
% end
        default:
            return panda::compiler::DataType::Type::ANY;
    }

    return panda::compiler::DataType::Type::ANY;
}

inline const char *AnyTypeTypeToString(AnyBaseType any_type)
{
    static constexpr auto COUNT = static_cast<uint32_t>(AnyBaseType::COUNT);
    static constexpr std::array<const char *, COUNT> ANYBASETYPE_NAMES = {
        "UNDEFINED_TYPE",
% Common::plugins.each do |plugin_lang, plugin_opts|
% next unless plugin_opts["compiler_base_types"]
% next unless plugin_opts["compiler_base_types"]["list_types"]
%   plugin_opts["compiler_base_types"]["list_types"].each_key do |type_name|
        "<%= plugin_lang %>_<%= type_name %>",
%   end
% end
    };
    auto idx = static_cast<uint32_t>(any_type);
    ASSERT(idx < COUNT);
    return ANYBASETYPE_NAMES[idx];
}

inline bool TryCompareAnyTypePluginGen([[maybe_unused]] const panda::compiler::CompareAnyTypeInst *cati,
                                       [[maybe_unused]] panda::compiler::EncodeVisitor *enc) {

% Common::plugins.each_value do |plugin_opts|
% next unless plugin_opts["compiler_base_types"]
% next unless plugin_opts["compiler_base_types"]["func_compare_implementation_codegen"]
    if (<%= plugin_opts["compiler_base_types"]["func_compare_implementation_codegen"] %>(cati, enc)) {
        return true; // NOLINT(readability-simplify-boolean-expr)
    }
% end

    return false;
}

inline bool TryGetAnyTypeNamePluginGen([[maybe_unused]] const panda::compiler::GetAnyTypeNameInst *cati,
                                       [[maybe_unused]] panda::compiler::EncodeVisitor *enc) {

% Common::plugins.each_value do |plugin_opts|
% next unless plugin_opts["compiler_base_types"]
% next unless plugin_opts["compiler_base_types"]["func_get_name_implementation_codegen"]
    if (<%= plugin_opts["compiler_base_types"]["func_get_name_implementation_codegen"] %>(cati, enc)) {
        return true; // NOLINT(readability-simplify-boolean-expr)
    }
% end

    return false;
}

inline bool TryCastAnyTypeValuePluginGen([[maybe_unused]] const panda::compiler::CastAnyTypeValueInst *cati,
                                         [[maybe_unused]] panda::compiler::EncodeVisitor *enc) {

% Common::plugins.each_value do |plugin_opts|
% next unless plugin_opts["compiler_base_types"]
% next unless plugin_opts["compiler_base_types"]["func_cast_implementation_codegen"]
    if (<%= plugin_opts["compiler_base_types"]["func_cast_implementation_codegen"] %>(cati, enc)) {
        return true; // NOLINT(readability-simplify-boolean-expr)
    }
% end

    return false;
}

inline bool TryCastValueToAnyTypePluginGen([[maybe_unused]] const panda::compiler::CastValueToAnyTypeInst *cvai,
                                           [[maybe_unused]] panda::compiler::EncodeVisitor *enc) {

% Common::plugins.each_value do |plugin_opts|
% next unless plugin_opts["compiler_base_types"]
% next unless plugin_opts["compiler_base_types"]["func_cast_to_any_implementation_codegen"]
    if (<%= plugin_opts["compiler_base_types"]["func_cast_to_any_implementation_codegen"] %>(cvai, enc)) {
        return true; // NOLINT(readability-simplify-boolean-expr)
    }
% end

    return false;
}

inline bool TryAnyTypeCheckPluginGen([[maybe_unused]] panda::compiler::AnyTypeCheckInst *check_inst,
                                     [[maybe_unused]] panda::compiler::EncodeVisitor *enc) {

% Common::plugins.each_value do |plugin_opts|
% next unless plugin_opts["compiler_base_types"]
% next unless plugin_opts["compiler_base_types"]["func_any_type_check_implementation_codegen"]
    if (<%= plugin_opts["compiler_base_types"]["func_any_type_check_implementation_codegen"] %>(check_inst, enc)) {
        return true; // NOLINT(readability-simplify-boolean-expr)
    }
% end

    return false;
}

inline bool TryHclassCheckPluginGen([[maybe_unused]] panda::compiler::HclassCheckInst *check_inst,
                                     [[maybe_unused]] panda::compiler::EncodeVisitor *enc) {

% Common::plugins.each_value do |plugin_opts|
% next unless plugin_opts["compiler_base_types"]
% next unless plugin_opts["compiler_base_types"]["func_hclass_check_implementation_codegen"]
    if (<%= plugin_opts["compiler_base_types"]["func_hclass_check_implementation_codegen"] %>(check_inst, enc)) {
        return true; // NOLINT(readability-simplify-boolean-expr)
    }
% end

    return false;
}

inline bool TryIsDynHeapObject([[maybe_unused]] compiler::AnyBaseType type) {

% Common::plugins.each_value do |plugin_opts|
% next unless plugin_opts["compiler_base_types"]
% next unless plugin_opts["compiler_base_types"]["func_hclass_check_implementation_codegen"]
    if (<%= plugin_opts["compiler_base_types"]["func_is_dyn_heap_object"] %>(type)) {
        return true; // NOLINT(readability-simplify-boolean-expr)
    }
% end

    return false;
}

inline bool TryObjByIndexCheckPluginGen([[maybe_unused]] const panda::compiler::FixedInputsInst2 *check_inst,
                                        [[maybe_unused]] panda::compiler::EncodeVisitor *enc,
                                        [[maybe_unused]] LabelHolder::LabelId id) {

% Common::plugins.each_value do |plugin_opts|
% next unless plugin_opts["compiler_base_types"]
% next unless plugin_opts["compiler_base_types"]["func_obj_by_index_check_implementation_codegen"]
    if (<%= plugin_opts["compiler_base_types"]["func_obj_by_index_check_implementation_codegen"] %>(check_inst, enc, id)) {
        return true; // NOLINT(readability-simplify-boolean-expr)
    }
% end

    return false;
}

inline bool TryLoadConstantPoolGen([[maybe_unused]] const panda::compiler::Inst *inst, 
                                   [[maybe_unused]] panda::compiler::EncodeVisitor *enc) {

% Common::plugins.each_value do |plugin_opts|
% next unless plugin_opts["compiler_base_types"]
% next unless plugin_opts["compiler_base_types"]["func_load_constant_pool"]
    if (<%= plugin_opts["compiler_base_types"]["func_load_constant_pool"] %>(inst, enc)) {
        return true; // NOLINT(readability-simplify-boolean-expr)
    }
% end
    return false;
}

inline bool TryLoadLexicalEnvGen([[maybe_unused]] const panda::compiler::Inst *inst, 
                                 [[maybe_unused]] panda::compiler::EncodeVisitor *enc) {

% Common::plugins.each_value do |plugin_opts|
% next unless plugin_opts["compiler_base_types"]
% next unless plugin_opts["compiler_base_types"]["func_load_lexical_env"]
    if (<%= plugin_opts["compiler_base_types"]["func_load_lexical_env"] %>(inst, enc)) {
        return true; // NOLINT(readability-simplify-boolean-expr)
    }
% end
    return false;
}

} // namespace panda::compiler

#endif // COMPILER_OPTIMIZER_TEMPLATES_IR_DYN_BASE_TYPES_H
